Eventstroom
Wanneer een gebeurtenis wordt aangeroepen, stroomt de gebeurtenis door de DOM en worden dezelfde gebeurtenissen afgevuurd op andere knooppunten en JavaScript-objecten. De gebeurtenisstroom kan worden geprogrammeerd als:
- een capture-fase (dat wil zeggen, vanaf de DOM boomstam naar de takken) of
- een bubbling-fase of terugkoppelfase (dat wil zeggen, vanaf de DOM boomtakken naar de boomstam, of
- beide;
Beschrijving
Wanneer je klikt op de anker-tag, stroomt de klikgebeurtenis naar beneden in de boomstructuur van het document. Een keer aangekomen op het element die de klikgebeurtenis heeft afgevuurd maakt het klikevent rechtsomkeer en stroom terug naar boven naar het moederelement.
capture- en bubbling-fase
Als je op een dom element klikt begint de capture bij het moederelement window
, stroomt door naar document
, dan naar html
, dan naar body
en dan naar het eerste element in het body
element en zo verder naar beneden in de DOM boomstructuur. Dat eerste naar beneden stromen heet de "capture-fase".
Het tweede naar boven stromen heet de terugkoppelfase of bubbling-fase. De volgende afbeelding moet dit verduidelijken:
Als je op de anker-tag klikt, berekent de DOM het capture-pad en vuurt de capture-fase event handlers af van het root document, body
, div
en a
. Dan maakt het rechtsomkeer en vuurt alle events handlers van de bubbling-fase af in de omgekeerde volgorde van de capture-fase.
Zodra de event tot de bovenkant van de DOM is opgeborreld, wordt het standaardgedrag van de browser uitgevoerd. In het geval van een anker-tag, wordt de gebruiker omgeleid naar een andere pagina.
Je moet goed begrijpen dat de events dit pad doorheen de betrokken elementen op en neer volgen. De twee fasen kunnen elk een afzonderlijke verzameling van event handlers hebben op elk knooppunt van in het pad.
Het pad en de volgorde waarin het event de bovenliggende keten doorloopt wordt bepaald voordat er ook maar één event handler wordt afgevuurd. Dit betekent dat het wijzigen van de elementen binnen een event handler geen invloed meer heeft op welke event handlers van welke elementen afgevuurd worden. Als bijvoorbeeld event handler een element zijn bovenliggende element verwijderd en zichzelf toevoegt aan het onderliggende element, zal de event handler van het verwijderde element toch nog worden afgevuurd.
Om het pad te illustreren geven we het volgende voorbeeld. In het onload
event van de pagina voeren we volgende code uit.
HTML
klik<div> hier voor div target<br /> <a href="#">en hier voor a target</a> </div>
CSS
div {
color: red;
text-decoration: underline;
cursor: pointer
}
JS
var eventstream = function() { //1 capture phase window.addEventListener('click', function() { console.log('1: capture fase window'); }, true); //2 capture phase document.addEventListener('click', function() { console.log('2: capture fase document'); }, true); //3 capture phase voor html element document.documentElement.addEventListener('click', function() { console.log('3: capture fase html'); }, true); //4 capture phase document.body.addEventListener('click', function() { console.log('4: capture fase body'); }, true); //5 capture phase document.querySelector('div').addEventListener('click', function() { console.log('5: capture fase div'); }, true); //6 target phase occurs during capture phase document.querySelector('a').addEventListener('click', function() { console.log('6: target fase a'); }, true); //7 target phase occurs during bubbling phase document.querySelector('a').addEventListener('click', function() { console.log('7: target fase a'); }, false); //8 bubbling phase document.querySelector('div').addEventListener('click', function() { console.log('8: bubbling fase div'); }, false); //9 bubbling phase document.body.addEventListener('click', function() { console.log('9: bubbling fase body'); }, false); //10 bubbling phase document.documentElement.addEventListener('click', function() { console.log('10: bubbling fase html'); }, false); //11 bubbling phase document.addEventListener('click', function() { console.log('11: bubbling fase document'); }, false); //12 bubbling phase window.addEventListener('click', function() { console.log('12: bubbling fase window') }, false); } window.onload = eventstream;
Als we op het anker element klikken zien we dat de events van alle noden op het pad, zowel tijdens de capture, target als bubbling fasen, afgevuurd worden:
"1: capture fase window"
"2: capture fase document"
"3: capture fase html"
"4: capture fase body"
"5: capture fase div"
"6: target fase a"
"7: target fase a"
"8: bubbling fase div"
"9: bubbling fase body"
"10: bubbling fase html"
"11: bubbling fase document"
"12: bubbling fase window"
Het zelfde gebeurd als op de div klikken. Maar het ankerelement wordt deze keer niet in het pad opgenomen:
"1: capture fase window" "2: capture fase document" "3: capture fase html" "4: capture fase body" "5: capture fase div" "8: bubbling fase div" "9: bubbling fase body" "10: bubbling fase html" "11: bubbling fase document" "12: bubbling fase window"
Je kan deze code uittesten op CodePen. Klik rechtsboven op Edit on CodePen en open het console venster (links onderaan) om deze oefening te kunnen volgen.
Bronnen
Giulio Mainardi, What Is Event Bubbling in JavaScript? Event Propagation Explained, May 24, 2017
Brian Moschel, A crash course in how DOM events work, October 01, 2010